#include "GameLib.h"
#include "Logger.h"
#include "../OpenGL.h"
#include <assert.h>
#include "Shapes3D/Vector3D.h"

GameLib gameLib;

void	GameLib::setColor(int hexColor){
    float r,g,b;
    r=(float)((hexColor&0xFF0000)/(float)0xFF0000);
    g=(float)((hexColor&0x00FF00)/(float)0x00FF00);
    b=(float)((hexColor&0x0000FF)/(float)0x0000FF);
    glColor3f(r,g,b);
}
void	GameLib::setColorA(float *colors){
    glColor4fv(colors);
}
void	GameLib::getColorA(float *colors){
    glGetFloatv(GL_CURRENT_COLOR,colors);
}
void	GameLib::setLineWidth(double width){
    glLineWidth((GLfloat)width);
}
double	GameLib::getLineWidth(){
    double getIt;
    glGetDoublev(GL_LINE_WIDTH,&getIt);
    return getIt;
}
void	GameLib::setPointSize(double size){
    glPointSize((float)size);
}
long	GameLib::mapL(long x, long in_min, long in_max, long out_min, long out_max){
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
int		GameLib::mapI(int x, int in_min, int in_max, int out_min, int out_max){
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float	GameLib::mapF(float x, float in_min, float in_max, float out_min, float out_max){
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
double	GameLib::mapD(double x, double in_min, double in_max, double out_min, double out_max){
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void	GameLib::screenToGL(Point2D &screen, Point3D &toGL){
    GLint viewport[4];
    GLdouble modelview[16];
    GLdouble projection[16];
    GLfloat winX, winY, winZ;
    GLdouble posX, posY, posZ;
    
    glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
    glGetDoublev( GL_PROJECTION_MATRIX, projection );
    glGetIntegerv( GL_VIEWPORT, viewport );
    
    winX = (float)screen.getX();
    winY = (float)(viewport[3] - screen.getY());
    glReadPixels( (int)screen.getX(), int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
    //mainLog.print(Str()<<"Win coords = ("<<winX<<", "<<winY<<", "<<winZ<<")");
    
    gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
    toGL.set(posX,posY,posZ);
}
void	GameLib::setShine(double amount){
    glMaterialf(GL_FRONT, GL_SHININESS, (float)amount);
}
void	GameLib::calculateNormalTriangle(Point3D &va,Point3D &vb,Point3D &vc,Direction3D &normal){
    Vector3D u,v,normalV;
    u.set(va,vb);
    v.set(vb,vc);
	normalV.cross(u,v);
    normalV.direction(normal);
}
void GameLib::calculateNormalPlane(Point3D &va, Point3D &vb, Point3D &vc, Point3D &vd, Direction3D &normal){
	Vector3D u,v,a,b;
	u.set(vb,va);
	v.set(vc,va);
	a.cross(u,v);
	u.set(vd,vc);
	v.set(va,vc);
	b.cross(u,v);
	a+=b;
	a.direction(normal);
}
void	GameLib::setNormal(Direction3D normal){
    glNormal3f(normal.getI(), normal.getJ(), normal.getK());
}
void	GameLib::errListener(const char *whosFault){
    GLenum errCode = glGetError();
    if (errCode==GL_NO_ERROR){return;}
    Str err;
    err<<"GL ERROR - ["<<(int)errCode<<"] with <"<<whosFault<<"> - ";
    switch (errCode)
    {
        case GL_INVALID_ENUM:
            err<<"GL_INVALID_ENUM: GLenum argument out of range";
            break;
        case GL_INVALID_VALUE:
            err<<"GL_INVALID_OPERATION: Numeric argument out of range";
            break;
        case GL_INVALID_OPERATION:
            err<<"GL_INVALID_OPERATION: Operation illegal in current state";
            break;
        case GL_STACK_OVERFLOW:
            err<<"GL_STACK_OVERFLOW: Command would cause a stack overflow";
            break;
        case GL_STACK_UNDERFLOW:
            err<<"GL_STACK_UNDERFLOW: Command would cause a stack underflow";
            break;
        case GL_OUT_OF_MEMORY:
            err<<"GL_OUT_OF_MEMORY: Not enough memory left to execute command";
            break;
        default:
            err<<"UNKNWON: number "<<(int)errCode;
            break;
    }
    errLog.print(err);
    abort();
}
void	GameLib::pushColor()
{
	Sclr cc;
	getColorA(cc.c);
	colorPushPop.push_back(cc);
}
void	GameLib::popColor()
{
	Sclr cc;
	cc=colorPushPop.back();
	colorPushPop.pop_back();
}
